/*
 * Copyright (c) 2025 Li Auto Inc. and its affiliates
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef SRC_CPP_RTPS_TRANSPORT_UDSSTREAMCHANNELRESOURCE_H_
#define SRC_CPP_RTPS_TRANSPORT_UDSSTREAMCHANNELRESOURCE_H_

#include <asio.hpp>
#include <ertps/utils/LocatorAlias.h>
#include <transport/ChannelResource.h>
#include <transport/ReceiverResource.h>
#include <transport/UDSStreamTransportDescriptor.h>
#include <mutex>
#include <edds/rtps/common/Types.h>

using namespace evbs;
using AsyncSendHandler = std::function<void(const asio::error_code&, size_t)>;

namespace vbs {
namespace transport {

class UDSStreamTransportInterface;

enum eSocketErrorCodes { eNoError, eBrokenPipe, eAsioError, eSystemError, eException, eConnectionAborted = 125 };

class UDSStreamChannelResource : public ChannelResource {
 private:
    asio::io_service& service_;
    std::mutex send_mutex_;
    std::shared_ptr<asio::local::stream_protocol::socket> socket_;
    std::shared_ptr<ReceiverResource> message_receiver_;

    UDSStreamChannelResource(const UDSStreamChannelResource&) = delete;

    UDSStreamChannelResource& operator=(const UDSStreamChannelResource&) = delete;

    enum UDSConnectionType { UDS_ACCEPT_TYPE = 0, UDS_CONNECT_TYPE = 1 };

    enum UDSConnectionStatus {
        eDisconnected = 0,
        eConnecting,              // Output -> Trying connection.
        eConnected,               // Output -> Send bind message.
        eWaitingForBind,          // Input -> Waiting for the bind message.
        eWaitingForBindResponse,  // Output -> Waiting for the bind response message.
        eEstablished,
        eUnbinding
    };

    UDSStreamTransportInterface* parent_;
    Locator locator_;
    Locator remote_locator_;
    std::mutex read_mutex_;
    std::atomic<UDSConnectionStatus> connection_status_;
    UDSConnectionType connection_type_;
    bool is_server_ = false;

 public:
    UDSStreamChannelResource(UDSStreamTransportInterface* parent, asio::io_service& service, const Locator& locator,
                             const Locator& remote_locator, uint32_t maxMsgSize);

    UDSStreamChannelResource(UDSStreamTransportInterface* parent, asio::io_service& service, const Locator& locator,
                             uint32_t maxMsgSize);

    UDSStreamChannelResource(UDSStreamTransportInterface* parent, asio::io_service& service,
                             std::shared_ptr<asio::local::stream_protocol::socket> socket, const Locator& locator,
                             const Locator& remote_locator, uint32_t maxMsgSize, bool is_server = false);

    virtual ~UDSStreamChannelResource();

    friend class UDSStreamTransportInterface;

    inline ReceiverResource* message_receiver() { return message_receiver_.get(); }

    inline UDSConnectionStatus change_status(UDSConnectionStatus s) {
        UDSConnectionStatus old = connection_status_.exchange(s);
        return old;
    }

    inline std::shared_ptr<asio::local::stream_protocol::socket> socket() { return socket_; }

    void disable() override;

    bool connect(const std::shared_ptr<UDSStreamChannelResource>& myself);

    void disconnect();

    uint32_t read(ertps::rtps::octet* buffer, std::size_t size, asio::error_code& ec);

    size_t send(const ertps::rtps::octet* header, size_t header_size, const ertps::rtps::octet* data, size_t size,
                asio::error_code& ec);

    void async_send(const ertps::rtps::octet* header, size_t header_size, const ertps::rtps::octet* data, size_t size,
                    AsyncSendHandler handler, bool is_default_header = false);

    asio::local::stream_protocol::endpoint remote_endpoint() const;

    asio::local::stream_protocol::endpoint local_endpoint() const;

    void set_options(const UDSStreamTransportDescriptor* options);

    void cancel();

    void close();

    void shutdown(asio::socket_base::shutdown_type what);

    bool connection_established() { return connection_status_ == UDSConnectionStatus::eEstablished; }

    UDSConnectionStatus connection_status() { return connection_status_; }

    inline const Locator& locator() const { return locator_; }
    inline const Locator& remote_locator() const { return remote_locator_; }

    UDSConnectionType uds_connection_type() const { return connection_type_; }
};

}  // namespace transport
}  // namespace vbs

#endif
